package log

import (
	"dev.ixmxm.top/rms/utils/buffer"
	"dev.ixmxm.top/rms/utils/file"
	"fmt"
	"os"
	"path"
	"time"
)

func NewFileEncodeWriter(cfg *Config) EncodeWriter {
	lf := &fileEncodeWriter{cfg: cfg}
	lf.startClean()
	return lf
}

type fileEncodeWriter struct {
	fi      *os.File
	cfg     *Config
	logDate string
}

func (v *fileEncodeWriter) Encoder(entry Entry) (*buffer.Buffer, error) {
	line := fmt.Sprintf("[%s] [%s] [%s] [%s] [%s] %s\n", entry.LoggerName, entry.Time.Local().Format(time.RFC3339Nano), entry.Level.CapitalString(), entry.TraceID, entry.Caller, entry.Message)
	buf := buffer.Get()
	defer buf.Free()

	if entry.Stack != "" {
		line += entry.Stack
		line += "\n"
	}
	buf.AppendString(line)
	return buf, nil
}

func (v *fileEncodeWriter) Write(p []byte) (int, error) {
	if !v.rotateLogFile() {
		return len(p), fmt.Errorf("rotate file error")
	}
	return v.fi.Write(p)
}

func (v *fileEncodeWriter) rotateLogFile() bool {
	//0.0若日期变更
	//0.1若日志文件不存在，则创建
	freeSpace := file.DiskFreeCheck()
	day := time.Now().Format("2006-01-02")

	switch {
	case freeSpace < v.cfg.file.minDiskSpaceSize:
		fmt.Println(ColorSprintf(Red, "Disk free space is %dMB, less than %dMB, stop write log to file.", freeSpace, v.cfg.file.minDiskSpaceSize))
		return false
	case v.logDate != day:
		v.logDate = day
	case !file.Exist(v.logFilePath()):
	default:
		return true
	}
	if err := v.makeLogFile(); err != nil {
		return false
	}
	return true
}

func (v *fileEncodeWriter) logFileDir() string {
	return path.Join(v.cfg.file.basePath, v.cfg.app)
}

func (v *fileEncodeWriter) logFilePath() string {
	return path.Join(v.logFileDir(), v.logDate+".log")
}

func (v *fileEncodeWriter) makeLogFile() error {
	f, err := file.OpenFile(v.logFilePath(), true, true)
	if err != nil {
		fmt.Println("Open log file failed:", err.Error())
		return err
	}
	v.fi = f
	return nil
}

func (v *fileEncodeWriter) startClean() {
	go func() {
		for {
			logs, _ := file.RecursiveErgodicExtFile(v.logFileDir(), "log")
			for _, item := range logs {
				if time.Now().Sub(item.ModTime()) > time.Duration(v.cfg.file.maxAge)*24*time.Hour {
					file.Remove(item.AbsolutePath)
				}
			}
			time.Sleep(3600 * time.Second)
		}
	}()
}
